import base64
import configparser
import os
import re
import shutil
import sys
import time
import tempfile
from time import sleep

import requests
from subprocess import Popen, PIPE

from PySide6.QtCore import Signal, QObject


# bucket_base_url = "https://jdcloud-kf.s3.cn-north-1.jdcloud-oss.com/app_key"
base_url = "https://gitee.com/south-joe/pal-tools-gui/raw/master"
# bucket_base_url = "http://localhost/app_key"


# def get_file_encoding(file: str):
#     with open(file, "rb") as f:
#         raw_data = f.read()
#         return detect(raw_data)["encoding"]


def build_result(code, data=None, msg="ok"):
    return {"code": code, "data": data, "msg": msg}


def build_config(data: dict):
    if not data:
        return ""
    config_str = "[/Script/Pal.PalGameWorldSettings]\nOptionSettings=("
    for i in data.keys():
        config_str += f"{i}={str(data[i])},"
    new_config_str = re.sub(",$", "", config_str) + ")"
    return new_config_str

def pal_config_parser(config_str=""):
    config_parser = configparser.ConfigParser()
    # if filename:
    #     config_parser.read(filename, encoding=encoding)
    #     result = (config_parser.get("/Script/Pal.PalGameWorldSettings", "OptionSettings"))
    # else:
    config_parser.read_string(config_str)
    result = (config_parser.get("/Script/Pal.PalGameWorldSettings", "OptionSettings"))
    return result

def pal_config_str2dict(config_str: str):
    config_str = config_str.replace("\n", "")
    config_str = re.sub(r"^\(", "", config_str)
    config_str = re.sub(r"\)$", "", config_str)
    params = requests.get(f"{base_url}/tuple_params.json").json()['data_list']
    key_list = []
    for i in params:
        kv_str = re.search(rf"{i}=\([a-zA-Z0-9]+(,[a-zA-Z0-9]+)*\)", config_str)
        if kv_str:
            tuple_str = kv_str.group().split("=")
            key_list.append(tuple_str[0])
            config_str = config_str.replace(tuple_str[1], tuple_str[1].replace(",", "|"))
    pal_config_list = config_str.split(",")
    config_data = dict()
    for i in pal_config_list:
        kv_list = i.split("=")
        if kv_list[0] in key_list:
            config_data[kv_list[0]] = kv_list[1].replace("|", ",")
        else:
            config_data[kv_list[0]] = kv_list[1]
    return config_data


def get_config_from_url():
    base64_config = requests.get(f"{base_url}/app_key").text[8:]
    config_str = base64.b64decode(base64_config).decode("utf-8")
    return config_str


def get_app_version():
    app_version = requests.get(f"{base_url}/app_version").text
    return round(float(app_version), 2)


class ServiceController(QObject):
    result = Signal(dict)
    download_finished = Signal(dict)
    finished = Signal()
    def __init__(self, steamcmd_path: str):
        super().__init__()
        self.download_base_url = None
        self.new_version = None
        self.is_multitasking = None
        self.config_str = None
        self.platform = True
        self.app_version = int()
        self._platform = ""
        self.config = ""
        self.config_path = ""
        self.recovery_archive_path = ""
        self.steamcmd_path = steamcmd_path
        self._platform = ""
        if sys.platform.startswith("linux"):
            self.pal_config_file_path = (f"{self.steamcmd_path}/steamapps/common/PalServer/Pal/Saved/Config"
                                         f"/LinuxServer/PalWorldSettings.ini")
            self._platform = "linux"
        elif sys.platform.startswith("win"):
            self.pal_config_file_path = (f"{self.steamcmd_path}/steamapps/common/PalServer/Pal/Saved/Config"
                                         f"/WindowsServer/PalWorldSettings.ini")
            self._platform = "windows"
        else:
            self.platform = False
        self.exec_func = None
        if self._platform == "windows":
            from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW, SW_HIDE
            self.startupinfo = STARTUPINFO()
            self.startupinfo.dwFlags |= STARTF_USESHOWWINDOW
            self.startupinfo.wShowWindow = SW_HIDE

    def download(self):
        try:
            self.download_base_url = f"https://gitee.com/south-joe/pal-tools-gui/releases/download/%E5%B8%95%E9%B2%81%E5%AE%9D%E5%85%B8v{self.new_version}"
            download_url = f"{self.download_base_url}/PalToolsGUI-Installer-v{self.new_version}.exe"
            response = requests.get(download_url, stream=True)
            if response.status_code == 200:
                total_size = int(response.headers.get("content-length", 0))
                block_size = 1024
                current_size = 0
                with open(f"{tempfile.gettempdir()}/PalToolsGUI_Installer.exe", "wb") as f:
                    for i in response.iter_content(block_size):
                        f.write(i)
                        current_size += block_size
                        self.result.emit(build_result(0, {
                            "result": round(current_size / total_size * 100, 2)
                        }))
                self.download_finished.emit(build_result(0))
            else:
                self.download_finished.emit(build_result(-1, msg=response.text))
        except Exception as e:
            self.result.emit(build_result(-1, msg=f"运行错误：{e}"))
        for i in range(3):
            sleep(1)

    def check(self):
        try:
            self.new_version = get_app_version()
            if self.new_version > self.app_version:
                data = { "result": True, "new_version": self.new_version }
            else:
                data = { "result": False }
            self.result.emit(build_result(0, data=data))
        except Exception as e:
            self.result.emit(build_result(-1, msg=f"运行错误：{e}"))
        self.finished.emit()

    def exec(self):
        self.is_multitasking = False
        if not self.steamcmd_path:
            self.result.emit(build_result(1, {
                "stderr": "SteamCMD安装目录不能为空！"
            }, "SteamCMD安装目录不能为空！"))
        if not self.platform:
            self.result.emit(build_result(-1, {
                "stderr": "不支持的平台！"
            }, "platform is unknown!"))
        if callable(self.exec_func):
            self.exec_func()
        elif isinstance(self.exec_func, list):
            self.is_multitasking = True
            for i in self.exec_func:
                sleep(1)
                if callable(i):
                    i()
        else:
            self.result.emit(build_result(-1, {
                    "stderr": "exec_func未定义或不是可执行函数"
                }, "exec_func is undefined or not is callable!"))

    def init_config_template(self):
        try:
            self.config_str = get_config_from_url()
            # print(self.config_str)
            if "OptionSettings" not in self.config_str:
                self.result.emit(build_result(-1, {
                    "stderr": ""
                }, f"获取配置文件失败！"))
                return
            with open(self.pal_config_file_path, "r", encoding="utf-8") as f:
                config_str = pal_config_parser(f.read())
            config_dict = pal_config_str2dict(config_str)
            self.result.emit(build_result(0, config_dict))
        except UnicodeDecodeError as e:
            with open(self.pal_config_file_path, "r", encoding="GBK") as f:
                config_str = pal_config_parser(f.read())
            config_dict = pal_config_str2dict(config_str)
            self.result.emit(build_result(0, config_dict))
        except (configparser.NoSectionError, FileNotFoundError, OSError) as e:
            config_str = pal_config_parser(config_str=self.config_str)
            config_dict = pal_config_str2dict(config_str)
            self.result.emit(build_result(0, config_dict))
        except Exception as e:
            self.result.emit(build_result(-1, {
                "stderr": str(e)
            }, f"获取配置文件失败！"))
        self.finished.emit()

    def check_pal_server(self):
        exec_command = []
        if self._platform == "linux":
            exec_command = Popen(["ps", "aux"],stdout=PIPE, stderr=PIPE, encoding="utf-8")
        elif self._platform == "windows":
            exec_command = Popen("tasklist",stdout=PIPE, stderr=PIPE, encoding="gbk", startupinfo=self.startupinfo)
        if not exec_command:
            return exec_command
        output, error = exec_command.communicate()
        result = []
        if not error:
            search_process = re.findall(".*PalServer.*", output.encode("utf-8").decode("utf-8"))
            if len(search_process) == 0:
                return search_process
            for i in search_process:
                if i:
                    result.append(re.sub(" +", " ", i).split(" "))
            return result
        else:
            return error.encode("utf-8").decode("utf-8")

    def enum_windows_callback(self, hwnd, result):
        if self._platform == "windows":
            import win32gui
            import win32con
            window_title = win32gui.GetWindowText(hwnd)
            if "PalServer-Win64" in window_title:
                win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
            return True

    def hide_window(self):
        if self._platform == "windows":
            import win32gui
            result = 0
            win32gui.EnumWindows(self.enum_windows_callback, result)
            return result

    def start_pal_server(self):
        try:
            if len(self.check_pal_server()) == 0:
                if self._platform == "linux":
                    uid = os.getuid()
                    if uid == 0:
                        self.result.emit(build_result(4, {
                            "stderr": "请在普通用户运行软件！"
                        }, msg="帕鲁服务无法在root用户下运行！"))
                        if not self.is_multitasking:
                            self.finished.emit()
                        return
                    Popen(f"{self.steamcmd_path}/steamapps/common/PalServer/PalServer.sh",
                          encoding="utf-8", creationflags=0)
                elif self._platform == "windows":
                    import win32gui
                    import win32con
                    Popen(f"{self.steamcmd_path}/steamapps/common/PalServer/PalServer.exe",
                          encoding="gbk", creationflags=8, startupinfo=self.startupinfo)
                    sleep(5)
                    self.hide_window()
                self.result.emit(build_result(0, msg="帕鲁服务已启动！"))
            else:
                self.result.emit(build_result(1, {
                    "stderr": "不能重复运行！"
                }, msg="帕鲁服务正在运行，跳过启动操作！"))
        except FileNotFoundError as e:
            self.result.emit(build_result(2, data={
                "stderr": str(e)
            }, msg="未找到执行文件！"))
        except Exception as e:
            self.result.emit(build_result(3, data={
                "stderr": str(e)
            }, msg="软件出现未知异常！"))
        if not self.is_multitasking:
            self.finished.emit()

    def kill_pal_server(self):
        try:
            pid = self.check_pal_server()
            if len(pid) > 0:
                for i in pid:
                    if self._platform == "linux":
                        Popen(["kill", "-9", i[1]],
                              encoding="utf-8", creationflags=0)
                    elif self._platform == "windows":
                        from subprocess import DETACHED_PROCESS
                        Popen(f"taskkill /PID {i[1]} /F",
                              encoding="gbk", creationflags=DETACHED_PROCESS, startupinfo=self.startupinfo)
                self.result.emit(build_result(0, msg="帕鲁服务已关闭！"))
            else:
                self.result.emit(build_result(1, {
                "stderr": "服务未运行，无法操作停止！"
            },msg="帕鲁服务未运行，跳过停止操作！"))
        except FileNotFoundError as e:
            self.result.emit(build_result(2, data={
                "stderr": str(e)
            }, msg="未找到执行文件！"))
        except Exception as e:
            self.result.emit(build_result(3, data={
                "stderr": str(e)
            }, msg="软件出现未知异常！"))
        if not self.is_multitasking:
            self.finished.emit()

    def update_pal_config(self, config: str):
        try:
            with open(self.pal_config_file_path, "w", encoding="utf-8") as f:
                f.write(config)
            self.result.emit(build_result(0, msg="写入配置文件成功！"))
        except FileNotFoundError as e:
            self.result.emit(build_result(1, {
                "stderr": str(e)
            }, msg="配置文件未找到！"))
        except Exception as e:
            self.result.emit(build_result(2, {
                "stderr": str(e)
            }, msg="软件出现未知异常！"))
        self.finished.emit()

    def update_games(self):
        try:
            self.result.emit(build_result(0, msg="帕鲁服务正在更新！请等待服务重新启动完成，大概需要5-30分钟！"))
            exec_command = None
            if self._platform == "linux":
                exec_command = Popen([f"{self.steamcmd_path}/steamcmd.sh", "+login", "anonymous",
                                      "+app_update", "2394010", "validate", "+quit"],
                      encoding="utf-8", stdout=PIPE, stderr=PIPE)
            elif self._platform == "windows":
                exec_command = Popen(f"{self.steamcmd_path}/steamcmd.exe +login anonymous "
                                     f"+app_update 2394010 validate +quit",
                      encoding="gbk", stdout=PIPE, stderr=PIPE, startupinfo=self.startupinfo)
            if exec_command:
                while True:
                    stdout = exec_command.stdout.readline()
                    if stdout == "" and exec_command.poll() is not None:
                        self.result.emit(build_result(0, msg="帕鲁游戏已更新完成！如日志中有Error，则更新失败，请重新尝试更新！"))
                        break
                    if stdout:
                        self.result.emit(build_result(0, msg=stdout.encode("utf-8").decode("utf-8")))
        except FileNotFoundError as e:
            self.result.emit(build_result(2, data={
                "stderr": str(e)
            }, msg="未找到执行文件！"))
        except Exception as e:
            self.result.emit(build_result(3, data={
                "stderr": str(e)
            }, msg="软件出现未知异常！"))
        self.finished.emit()

    def recovery_archive(self, recovery_archive_path):
        try:
            backup_path = f"backup/world/{time.strftime("%Y-%m-%d_%H_%M_%S", time.localtime())}"
            app_path = "steamapps/common/PalServer/Pal/Saved/SaveGames/0"
            rc_path = f"{self.steamcmd_path}/{app_path}/{recovery_archive_path}"
            for i in os.listdir(rc_path):
                src_file = f"{self.steamcmd_path}/{app_path}/{recovery_archive_path}/{i}"
                dest_file = f"{self.steamcmd_path}/{app_path}/{recovery_archive_path.split("/")[0]}/{i}"
                os.makedirs(backup_path, exist_ok=True)
                # print(f"src={src_file};dest={dest_file};backup={backup_path}")
                if not os.path.isdir(src_file):
                    shutil.copy2(dest_file, f"{backup_path}/{i}")
                    self.result.emit(build_result(0, msg=f"配置文件{dest_file}备份至{backup_path}成功！"))
                    time.sleep(1)
                    shutil.copy2(src_file, dest_file)
                    self.result.emit(build_result(0, msg=f"源配置文件{src_file}覆盖至{dest_file}成功！"))
        except FileNotFoundError as e:
            self.result.emit(build_result(1, {
                "stderr": str(e)
            }, msg="配置文件未找到！"))
        except PermissionError as e:
            self.result.emit(build_result(3, {
                "stderr": str(e)
            }, msg="没有权限复制文件！"))
        except Exception as e:
            self.result.emit(build_result(2, {
                "stderr": str(e)
            }, msg="软件出现未知异常！"))
        self.finished.emit()
